/**
 * Created by oneminute on 7/24/14.
 */

var lz = require("../utils/modules")();
var rabbit = require("rabbit.js");
var socketio = require("socket.io");
var async = require("async");
var redisAdapter = require('socket.io-redis');
var redis = require("redis");

var rabbitContext = null;
var socket = null;
var chatServer = null;

exports.setup = function (_httpServer) {
    if (typeof (lz.User) !== "function") {
        lz = require("../utils/modules")();
    }
    if (!rabbitContext) {
        rabbitContext = rabbit.createContext(lz.Config.rabbit.url);
        rabbitContext.on('ready', function () {
            lz.Logger.debug("rabbit queue is ready.");
        });
    }

    //var pub = redis.createClient();
    //var sub = redis.createClient(null, null, {detect_buffers: true});
    //var srv = http();
    //var sio = io(srv, {adapter: redisAdapter({pubClient: pub, subClient: sub})});

    socket = socketio(_httpServer, {
        /*transports: [
         'polling',
         'websocket'
         ]*/
        //adapter: redisAdapter(lz.Config.db.redis)
        //adapter: redisAdapter({pubClient: pub, subClient: sub})
    });
    //socket.adapter(redisAdapter(lz.Config.db.redis));

    socket.on('connection', function (_socket) {
        chatServer = new ChatServer(_socket);
    });
};

function ChatServer(_socket) {
    this.socket = _socket;
    this.pub = null;
    this.sub = null;
    this.uid = null;
    this.ackTimers = {};
    var self = this;

    lz.Logger.info("ChatServer: 创建了一个新的连接(%s:%s)。");

    this.socket.on('disconnect', self.onDisconnect.bind(self));
    this.socket.on('iwant.connect', self.onIWantConnect.bind(self));
    this.socket.on('iwant.disconnect', self.onIWantDisconnect.bind(self));
    this.socket.on('iwant.message', self.onIWantMessage.bind(self));
    this.socket.on('error', self.onError.bind(self));
}

ChatServer.prototype.onError = function (_err) {
    lz.Logger.debug('chat:client:OnError: 发生了一个socket错误: ', _err);
};

ChatServer.prototype.setOffline = function (_callback) {
    lz.Logger.debug("ChatServer:setOffline: 调用。aid: ", this.aid);
    var self = this;
    var user = new lz.User({_id: self.aid});
    user.load({loadAll: false}, function (_err, _user) {
        if (_user) {
            user.setOnline(false, function (_err, _user) {
                lz.Logger.debug("chat: 用户%s下线。", user.name);
                self.aid = null;
                self.pub.close();
                self.sub.close();
                delete self.sub;
                delete self.pub;
                self.sub = null;
                self.pub = null;
                _callback();
            });
        }
    });
};

ChatServer.prototype.ack = function (_ack, _res, _reason, _mid) {
    if (_ack) {
        lz.Logger.debug("ChatServer:ack: res: %s, reason %s, mid %s", _res, _reason, _mid);
        _ack({
            res: _res,
            reason: _reason,
            mid: _mid
        });
        lz.Logger.debug("ChatServer:ack: 已返回应答消息。");
    }
};

ChatServer.prototype.onDisconnect = function () {
    lz.Logger.debug("ChatServer:onDisconnect: 用户关闭了一个连接。", this.aid);
    this.setOffline(function () {
        lz.Logger.debug("ChatServer:onDisconnect: 清除缓存。");
        delete self.socket;
        delete this;
    });
};

/**
 * 逻辑连接。
 *
 * @param _msg 必须有uid字段
 */
ChatServer.prototype.onIWantConnect = function (_msg, _ack) {
    lz.Logger.debug("ChatServer:onIWantConnect: 接收到用户逻辑连接请求。", _msg.uid);
    //lz.Logger.debug("ChatServer:onIWantConnect: 用户是否上传了_ack函数。", _ack);
    var self = this;
    var uid = "";
    try {
        uid = lz.Utils.decrypt(_msg.uid, true);
        _msg.uid = uid;
    } catch (e) {
        lz.Logger.error(e.stack);
        self.ack(_ack, false, "uid解析错误");
        return;
    }
    var user = new lz.User({id: uid});
    user.load({loadAll: false, method: "byId"}, function (_err, _user) {
        if (_user) {
            var alreadyOnline = _user.online;
            user.setOnline(true, function (_err) {
                lz.Logger.debug('ChatServer:onIWantConnect: 开始创建消息队列。');
                self.aid = user._id;
                lz.Logger.debug("ChatServer:onIWantConnect: 加载User对象(%s, %s)。", user.id, user.name);
                self.pub = rabbitContext.socket('PUB', {routing: 'topic'});

                self.sub = rabbitContext.socket('SUB', {routing: 'topic'});

                self.pub.connect('chat', function () {
                    lz.Logger.debug("ChatServer:onIWantConnect: pub connection: ");
                    self.sub.connect('chat', user.id, function () {
                        lz.Logger.debug("ChatServer:onIWantConnect: sub connection: ");
                        // 判断用户是否在线
                        if (alreadyOnline) {
                            lz.Logger.warn("ChatServer:onIWantConnect: 用户已在线。将强制关闭原在线用户。");
                            self.pub.setsockopt("topic", _msg.uid);
                            self.pub.write(JSON.stringify({
                                type: "exit",
                                uid: _msg.uid,
                                sid: self.socket.id
                            }), 'utf8');
                        }
                    });
                });
                self.sub.setEncoding('utf8');
                self.sub.on('data', self.onRabbitMQData.bind(self));
                self.sub.on('error', function (_msg) {
                    lz.Logger.error('chat:sub: error: ', _msg);
                });
                self.pub.on('error', function (_msg) {
                    lz.Logger.error('chat:pub: error: ', _msg);
                });

                var msg = new lz.Message({header: {recieverid: user.id}});
                msg.loadOfflineMessages(function (_err, _messages) {
                    lz.Logger.debug("ChatServer:onIWantConnect: offline messages: ", _messages);
                    if (_messages && Array.isArray(_messages) && _messages.length > 0) {
                        lz.Logger.debug("ChatServer:onIWantConnect: 用户有离线消息。");
                        self.ack(_ack, true);
                        self.socket.emit("iwant.connect", _messages, function (_res) {
                            lz.Logger.debug("ChatServer:onIWantConnect: 返回用户登陆确认信息后的ACK。");
                            var tasks = [];
                            _messages.forEach(function (_msg) {
                                tasks.push(function (_cb) {
                                    _msg.delete(function () {
                                        _cb(null);
                                    });
                                });
                            });
                            async.series(tasks, function () {
                                lz.Logger.debug("ChatServer:onIWantConnect: 删除掉用户的离线消息。");
                            });
                        });
                        lz.Logger.debug("ChatServer:onIWantConnect: 离线消息发送完毕。");
                    } else {
                        lz.Logger.debug("ChatServer:onIWantConnect: 用户没有离线消息。");
                        self.ack(_ack, true);
                    }
                });
            });
        } else {
            lz.Logger.warn("ChatServer:onIWantConnect: 用户提供的uid(%s)不存在。", _msg.uid);
            self.ack(_ack, false, "用户不存在");
        }
    });
};

/**
 * 逻辑断开连接。
 */
ChatServer.prototype.onIWantDisconnect = function (_msg) {
    lz.Logger.debug('ChatServer:onIWantDisconnect: 接收到用户逻辑断开请求。', _msg, this.aid);
    this.setOffline();
};

ChatServer.prototype.onIWantMessage = function (_msg, _ack) {
    lz.Logger.debug("ChatServer:onIWantMessage: 调用。", _msg);
    var self = this;
    var msg = new lz.Message(_msg);
    lz.Logger.debug("ChatServer:onIWantMessage: 消息: ", msg);
    msg.header.senderid = lz.Utils.decrypt(msg.header.senderid, true);
    msg.header.recieverid = lz.Utils.decrypt(msg.header.recieverid, true);
    var sender = new lz.User({id: msg.header.senderid});
    var reciever = new lz.User({id: msg.header.recieverid});
    lz.Logger.debug("ChatServer:onIWantMessage: sender: %s , reciever: %s .",
        sender.id, reciever.id);
    sender.load({method: "byId"}, function (_err, _user) {
        if (_err) {
            lz.Logger.error(_err);
            self.ack(_ack, false, "system error", _msg.header.mid);
            return;
        }
        if (!_user) {
            self.ack(_ack, false, "sender not existed", _msg.header.mid);
            return;
        }
        reciever.load({method: "byId"}, function (_err, _user) {
            lz.Logger.debug("ChatServer: onIWantMessage: reciever(%s) online: ", reciever.name, reciever.online);
            if (_err) {
                lz.Logger.error(_err);
                self.ack(_ack, false, "system error", _msg.header.mid);
                return;
            }
            if (!_user) {
                self.ack(_ack, false, "reciever not existed", _msg.header.mid);
                return;
            }
            lz.Logger.debug("ChatServer:onIWantMessage: 准备发布消息到MQ。");
            lz.Logger.debug("ChatServer:onIWantMessage: sender: ", sender.name);
            lz.Logger.debug("ChatServer:onIWantMessage: reciever: ", reciever.name);

            msg.save(function (_err, _msg) {
                if (_err) {
                    lz.Logger.error("ChatServer:onIWantMessage: 保存消息失败: ", _err);
                    self.ack(_ack, false, "save offline message error.", _msg.header.mid);
                }

                lz.Logger.debug("ChatServer:sendMessage: 保存消息完毕。", _msg);
                self.pub.setsockopt("topic", _msg.header.recieverid);
                self.pub.write(JSON.stringify({
                    type: "msg",
                    senderid: _msg.header.senderid,
                    id: _msg._id,
                    mid: _msg.header.mid
                }), 'utf8');
                lz.Logger.debug("ChatServer:sendMessage: 发送消息到消息队列: ", _msg);

                if (reciever.online) {
                    // 启动等待与监听任务，等待收到ACK或是超时。
                    self.waitForRabbitAck(_ack, _msg._id, _msg.header.mid);
                } else {
                    self.ack(_ack, false, "reciever offline", _msg.header.mid);
                }
            });
        });
    });
};

ChatServer.prototype.waitForRabbitAck = function (_ack, _id, _mid) {
    var self = this;
    var timerId = setTimeout(function () {
        self.ack(_ack, false, "send message timeout", _mid);
    }, 10000);
    this.ackTimers[_id] = {
        timerId: timerId,
        ack: _ack
    };
};

ChatServer.prototype.onRabbitMQData = function (_data) {
    lz.Logger.debug("ChatServer:onRabbitMQData: 调用。", _data);
    var data = JSON.parse(_data);
    switch (data.type) {
        case "ack":
            this.onRabbitMQAck(data);
            break;
        case "msg":
            this.onRabbitMQMsg(data);
            break;
        case "exit":
            this.onRabbitMQExit(data);
            break;
    }
};

ChatServer.prototype.onRabbitMQAck = function (_data) {
    var self = this;
    var timer = this.ackTimers[_data.id];
    clearTimeout(timer.timerId);
    if (_data.success) {
        self.ack(timer.ack, true, null, _data.mid);
    } else {
        self.ack(timer.ack, false, _data.reason, _data.mid);
    }
};

ChatServer.prototype.onRabbitMQMsg = function (_data) {
    var self = this;
    self.pub.setsockopt("topic", _data.senderid);
    var msg = new lz.Message({_id: _data.id});
    msg.load(function (_err, _msg) {
        if (_err || !_msg) {
            lz.Logger.error("ChatServer:onRabbitMQMsg: 从数据库中读取消息失败。", _data.id);
            self.pub.write(JSON.stringify({
                type: "ack",
                id: _data.id,
                mid: _data.mid,
                success: false,
                reason: "read message from db failure"
            }), 'utf8');
            return;
        }
        self.sendMessage(msg);
    });
};

ChatServer.prototype.onRabbitMQExit = function (_data) {
    var self = this;
    if (_data.sid != self.socket.id) {
        lz.Logger.debug("ChatServer:onRabbitMQExit: 将强制用户下线。");
        self.socket.emit("iwant.forceexit", {}, function () {
        });
        self.aid = null;
        self.pub.close();
        self.sub.close();
        delete self.sub;
        delete self.pub;
        self.sub = null;
        self.pub = null;
        lz.Logger.debug("ChatServer:onRabbitMQExit: 强制下线完成。");
    }
};

ChatServer.prototype.sendMessage = function (_msg) {
    var self = this;
    lz.Logger.debug("ChatServer:sendMessage: ", _msg);
    self.socket.emit("iwant.message", _msg, function (_ack) {
        if (_ack) {
            _msg.delete(function (_err) {
                self.pub.write(JSON.stringify({
                    type: "ack",
                    id: _msg._id,
                    mid: _msg.header.mid,
                    success: true
                }), 'utf8');
            });

        } else {
            self.pub.write(JSON.stringify({
                type: "ack",
                id: _msg._id,
                mid: _msg.header.mid,
                success: false,
                reason: "client ack error"
            }), 'utf8');
        }
    });
};
